package cn.edu.scau.cmi.ema.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import cn.edu.scau.cmi.ema.dao.*;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import cn.edu.scau.cmi.ema.domain.base.CmiPagination;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;

import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.persistence.Query;
import java.util.Iterator;
@Service("RulefieldService")
@Transactional

public class RulefieldServiceImpl extends CmiEmaService implements RulefieldService {
	public RulefieldServiceImpl() {
	}

	@Transactional
	public Rulefield saveRulefield(Rulefield rulefield) {
		Set<Rulecolumn> existingRulecolumnsForRulefield = new HashSet<Rulecolumn>();

		Set<Rulecolumn> selectedRulecolumnsForRulefield = rulefield.getRulecolumnsForRulefield();

		if (rulefield.getId() == null) {
			Rule rule = ruleDAO.findRuleById(rulefield.getRule().getId());

			Rulefield existingRulefield = rulefieldDAO.findRulefieldByPhysicalPrimaryKey(rule.getId(),
					rulefield.getEntityclassname(), rulefield.getEntityfieldname());

			rulefield.setRule(rule);

			if (isEquals(rulefield, existingRulefield)) {
				return existingRulefield;
			}

			rulefield = rulefieldDAO.store(rulefield);
			rulefieldDAO.flush();

			for (Rulecolumn relatedRulecolumn : selectedRulecolumnsForRulefield) {
				relatedRulecolumn.setRulefield(rulefield);
				rulecolumnDAO.store(relatedRulecolumn);
			}
		} else {
			Rulefield existingRulefield = rulefieldDAO.findRulefieldByPrimaryKey(rulefield.getId());

			existingRulecolumnsForRulefield = existingRulefield.getRulecolumnsForRulefield();

			Set<Rulecolumn> prepareDeleteRulecolumnsForRulefield = new HashSet<Rulecolumn>();
			Set<Rulecolumn> prepareAddRulecolumnsForRulefield = new HashSet<Rulecolumn>();

			prepareDeleteRulecolumnsForRulefield.addAll(selectedRulecolumnsForRulefield);
			prepareDeleteRulecolumnsForRulefield.addAll(existingRulecolumnsForRulefield);

			prepareAddRulecolumnsForRulefield.addAll(selectedRulecolumnsForRulefield);
			prepareAddRulecolumnsForRulefield.addAll(existingRulecolumnsForRulefield);

			prepareDeleteRulecolumnsForRulefield.removeAll(selectedRulecolumnsForRulefield);
			prepareAddRulecolumnsForRulefield.removeAll(existingRulecolumnsForRulefield);

			for (Rulecolumn relatedRulecolumn : prepareAddRulecolumnsForRulefield) {
				relatedRulecolumn.setRulefield(rulefield);
				rulecolumnDAO.store(relatedRulecolumn);
			}

			for (Rulecolumn relatedRulecolumn : prepareDeleteRulecolumnsForRulefield) {
				relatedRulecolumn.setRulefield(null);
				rulecolumnDAO.store(relatedRulecolumn);
			}
			rulefield = rulefieldDAO.store(rulefield);
			rulefieldDAO.flush();
		}

		rulecolumnDAO.flush();
		return rulefield;
	}

	public void deleteRulefield(Rulefield rulefield) {
		rulefieldDAO.remove(rulefield);
		rulefieldDAO.flush();
	}

	@Transactional
	public void deleteRulefields(Set<Integer> deleteRulefieldsId) {
		Query query = rulefieldDAO.createNamedQuery("deleteRulefieldsById", -1, -1);
		query.setParameter(1, deleteRulefieldsId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Rulefield> loadRulefields() {
		return rulefieldDAO.findAllRulefields();

	}

	@Transactional
	public Set<Rulefield> loadReferenceRulefields(Set<Rulefield> rulefields) {
		Iterator<Rulefield> rulefieldsIterator = rulefields.iterator();
		Set<Rulefield> referencedRulefields = new HashSet<>();
		while (rulefieldsIterator.hasNext()) {
			Rulefield currentRulefield = rulefieldsIterator.next();

			if (!currentRulefield.getRulecolumnsForRulefield().isEmpty()) {
				referencedRulefields.add(currentRulefield);
				continue;
			}
		}
		return referencedRulefields;
	}

	public Set<Rulefield> loadRulefieldsByIdSet(Set<Integer> rulefieldIdSet) {

		Iterator<Integer> rulefieldIdIterator = rulefieldIdSet.iterator();
		Set<Rulefield> rulefieldSet = new HashSet<>();
		while (rulefieldIdIterator.hasNext()) {
			Integer id = rulefieldIdIterator.next();
			Rulefield currentRulefield = rulefieldDAO.findRulefieldById(id);
			rulefieldSet.add(currentRulefield);
		}
		return rulefieldSet;
	}

	@Transactional
	public List<Rulefield> findAllRulefields(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Rulefield>(rulefieldDAO.findAllRulefields(startResult, maxRows));
	}

	@Transactional
	public Rulefield findRulefieldByPrimaryKey(Integer id) {
		return rulefieldDAO.findRulefieldByPrimaryKey(id);
	}

	@Transactional
	public Integer countRulefields() {
		return ((Long) rulefieldDAO.createQuerySingleResult("select count(o) from Rulefield o").getSingleResult())
				.intValue();
	}

	@Transactional
	public Integer countRulefields(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Rulefield.class);
		return ((Long) rulefieldDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 * 
	 */
	@Transactional
	public Integer countRulefieldsByName(String queryName) {
		Query query = rulefieldDAO.createNamedQuery("countRulefieldsByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countRulefieldsByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Rulefield.class);
		Session session = entityManager.unwrap(Session.class);
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadRulefieldObjectReferencedSet(Rulefield rulefield) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();
		if (rulefield.getRule() != null) {

			Set<Integer> ruleIds = new HashSet<Integer>();
			ruleIds.add(rulefield.getRule().getId());
			objectReferencedSetMap.put("ruleIds", ruleIds);
		}

		Set<Integer> rulecolumnsIds = new HashSet<Integer>();
		for (Rulecolumn obj : rulefield.getRulecolumnsForRulefield()) {

			rulecolumnsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("rulecolumnsIds", rulecolumnsIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadRulefieldClassReferencedSet(Rulefield rulefield) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("rules", ruleDAO.findAllRules());
		classReferencedSetMap.put("rulecolumns", rulecolumnDAO.findAllRulecolumns());

		return classReferencedSetMap;
	}

	public boolean isEquals(Rulefield rulefield, Object obj) {
		if (obj == rulefield)
			return true;
		if (!(obj instanceof Rulefield))
			return false;
		Rulefield comparedRulefield = (Rulefield) obj;
		if (!CheckPropertyEqual(rulefield.getRule().getId(), comparedRulefield.getRule().getId())) {
			return false;
		}
		if (!CheckPropertyEqual(rulefield.getEntityclassname(), comparedRulefield.getEntityclassname())) {
			return false;
		}
		if (!CheckPropertyEqual(rulefield.getEntityfieldname(), comparedRulefield.getEntityfieldname())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

